#pragma once

#include "Promise.h"

namespace AsyncLib
{

template<typename T>
class CFuture
{
public:
	using promise_type = CPromise<T>;

	static_assert(!std::is_array_v<T>, "Use std::array instead");
	static_assert(!std::is_rvalue_reference_v<T>);

	CFuture(std::shared_ptr<CAssocState<T>> pState) : m_pState(std::move(pState))
	{
		assert(m_pState);
	}

	CFuture(CFuture&&) = default;
	CFuture& operator=(CFuture&&) = default;

	[[nodiscard]] bool IsCompleted() const
	{
		return m_pState->IsCompleted();
	}

	[[nodiscard]] bool Wait() const
	{
		return m_pState->Wait();
	}

	[[nodiscard]] decltype(auto) Result() const
	{
		static_assert(!std::is_void_v<T>);
		return m_pState->GetValue();
	}

	void Forget() const
	{
		m_pState->Forget();
	}

	const CFuture& CaptureThread() const
	{
		m_pState->CaptureThread();
		return *this;
	}

	// Use static functions to avoid external calls
	static const auto& GetState(const CFuture& future)
	{
		return future.m_pState;
	}

private:
	std::shared_ptr<CAssocState<T>> m_pState;
};

// Delete some member functions so that it can only be co_await
template<>
class CFuture<nullptr_t>
{
	using T = nullptr_t;
public:
	using promise_type = CPromise<T>;

	CFuture(std::shared_ptr<CAssocState<T>> pState) : m_pState(std::move(pState))
	{
		assert(m_pState);
	}

	CFuture(const CFuture&) = delete;
	CFuture& operator=(const CFuture&) = delete;

	[[nodiscard]] bool IsCompleted() const
	{
		return m_pState->IsCompleted();
	}

	const CFuture& CaptureThread() const
	{
		m_pState->CaptureThread();
		return *this;
	}

	// Use static functions to avoid external calls
	static const auto& GetState(const CFuture& future)
	{
		return future.m_pState;
	}

private:
	std::shared_ptr<CAssocState<T>> m_pState;
};

} // namespace AsyncLib
